﻿using System;
using System.Net;
using Pfz.Threading;

namespace Pfz.Remoting
{
	/// <summary>
	/// Listener for Tcp/IP connections and channellers.
	/// </summary>
	public sealed class ChannellerListener:
		ThreadSafeDisposable,
		IChannellerListener
	{
		private IConnectionListener<IConnection> _listener;
		private readonly int _bufferSizePerChannel;
		private CryptographySide _cryptographySide;

		/// <summary>
		/// Creates a new listener on the given address, port and using the given bufferSizePerChannel.
		/// </summary>
		public ChannellerListener(IPAddress localAddress, int port, int bufferSizePerChannel, bool useCryptography=false):
			this(new DisposableTcpListener(localAddress, port), bufferSizePerChannel, useCryptography)
		{
		}

		/// <summary>
		/// Creates a new channeller listener over an existing Listener.
		/// </summary>
		public ChannellerListener(IConnectionListener<IConnection> listener, int bufferSizePerChannel, bool useCryptography=false)
		{
			if (listener == null)
				throw new ArgumentNullException("listener");

			if (useCryptography)
				_cryptographySide = CryptographySide.Server;

			_bufferSizePerChannel = bufferSizePerChannel;
			_listener = listener;
		}

		/// <summary>
		/// Releases the internal listener.
		/// </summary>
		protected override void Dispose(bool disposing)
		{
			if (disposing)
				Disposer.Dispose(ref _listener);

			base.Dispose(disposing);
		}

		/// <summary>
		/// Accepts a new channeller.
		/// </summary>
		public IChanneller TryAccept()
		{
			while(true)
			{
				var listener = _listener;
				if (listener == null)
					return null;

				var client = _listener.TryAccept();
				if (client == null)
					return null;

				try
				{
					return ChannellerConnection.Create(client, _bufferSizePerChannel, _cryptographySide);
				}
				catch
				{
					// if an exception happens here, probably it's a problem with cryptography but the listener is OK.
					// so we close the connection and keep listening.
					// If the problem is with the listener, we will get another exception and return false.
					client.Dispose();
				}
			}
		}
	}
}
